<?php
/**
 * PHP 文本缓存
 *
 * @filesource phpfiled.class.php
 * @package peck
 * @subpackage _cache
 * @version $id: 0.2, utf8, Mon Dec 14 12:07:01 CST 2009
 * @author LD King <kldscs[at]gmail.com>
 * @copyright Copyleft (D.) 2007 - 2009 MiFunny China Inc.
 * @link http://mifunny.info/
 */
//require_once '_dscache.class.php';
require_once '_file.class.php';
class _phpfiled{ // extends _dscache{
    
	/**
	 * 默认的缓存策略
	 *
	 * DSCACHE_LIFE_TIME - life_time: 缓存有效时间(秒), 默认值 900
     *    如果设置为 0 表示缓存总是存在,
	 *
	 * DSCACHE_ENCODING - encoding_key: 编码缓存文件名, 默认为 true
     *    默认使用 md5 编码文件名,
     *    设置为 false 则自动去除 ID 中的字符和空格.
     *
     * DIR_DEPTH - auto_split_dir: 自动根据MD5码分割目录, 默认 3  
     *    用于缓存文件过多的情况;
     *    当置>0时, 由$id中设置的分隔符无效;
     *
     * DSCACHE_DIR - cache_dir: 缓存目录（必须指定）
	 *    $id字段存在带有目录, 则会在缓存目录下创建子目录, 支持多级目录.
	 *    (建议不要在单目录下放置超过1000个文件)
     *
     * DSCACHE_UMASK - cache_dir_umask: 创建缓存目录的权限, 默认为 0700
     *
     * DSCACHE_DEPTH - depth: 文件夹深度.
     *
	 * @var array
	 */
	protected $_default_policy = array(
		'life_time'         => 900,
        'encoding_key' => false,
		'cache_dir'         => '/tmp',
        'cache_dir_umask'   => 0700,
        'dir_depth' => 3,
        'tag' => 'default',
	);
	
   	/**
	 * 构造函数
	 *
	 * @param 默认的缓存策略 $default_policy
	 */
	public function __construct(array $default_policy = null){
		if (!is_null($default_policy))
            $this->_default_policy = $this->_policy($default_policy);
	}

    /**
     * cache key is exists
     *
     * @param string $path
     * @return boolean
     */
    protected function exist($path){
        return is_file($path); 
    }

	/**
     * 写入缓存
     *   缓存时间为0, 永不过期
     *
     * @param string $id 缓存唯一标识ID
     * @param mixed $data 缓存内容
     * @param mixed $expiration 缓存时间,默认 int 900
     */
    public function set($id, $data, $expiration=null){
        $path = $this->_path($id);
        return $this->_set($path, $data);
    }

    protected function _set($path, $data){
        file_put_contents($path, '<?php die(); ?>'.serialize($data), LOCK_EX);
        clearstatcache();
        return true;
    }
    
    public function setByTag ($tag , $key , $value , $expiration=null ){
        $path = $this->_path($key, $tag);
        return $this->_set($path, $value);
    }

    /**
     * 当 key 存在的时候才 add item
     *
     */
    public function add($key, $value, $expiration=0){
        $path = $this->_path($key);
        if(!$this->exist($path)){
            return $this->_set($path, $value);
        }else
            return false;
    }
    
    public function addByTag ($tag, $key , $value , $expiration=null ){
        $path = $this->_path($key, $tag);
        if(!$this->exist($path)){
            return $this->_set($path, $value);
        }else
            return false;
    }

    /**
     * 替换， 如果不存在 返回false
     */
    public function replace ($key , $value , $expiration=null ){
        $path = $this->_path($key);
        if($this->exist($path)){
            return $this->_set($path, $value);
        }else
            return false;
    }

    public function replaceByTag ($tag , $key , $value , $expiration=null){
        $path = $this->_path($key, $tag);
        if($this->exist($path)){
            return $this->_set($path, $value);
        }else
            return false;
    }


	/**
     * 读取缓存，失败或缓存撒失效时返回 false
     *
     * @param string $id 缓存唯一标识ID
     * @param array $policy 配置数组
     *
     * @return mixed
     */
    public function get($id, $expiration=null){
        $path = $this->_path($id);

        return $this->_get($path, $expiration);
    }

    protected function _get($path, $expiration=null){
    	$life_time = is_null($expiration) ? $this->_default_policy['life_time'] : $expiration;

        if( is_file($path) ){
            if( $life_time==0 OR
              (time()-filemtime($path))<=$life_time ){
                return unserialize( file_get_contents($path,NULL,NULL,15) );   
            }else
                return false;
        }else
            return false;
    }

    public function getByTag ($tag , $key, $expiration=null){
        $path = $this->_path($key, $tag);
        return $this->_get($path, $expiration);
    }

	/**
	 * 删除指定的缓存
	 *
     * @param string $id 缓存唯一标识ID
     * @param int $delay 删除时间
	 */
	public function delete($id, $delay=0){
		$path = $this->_path($id);
        if (is_file($path)) {
            unlink($path);
        }
	}
    
    public function deleteByTag ($tag , $key , $time=null){
		$path = $this->_path($key, $tag);
        if (is_file($path)) {
            unlink($path);
        }
    }
	
    public function flush ($delay = 0){
        if(false !== stripos(PHP_OS, 'linux') ){
            exec('rm '.$this->_default_policy['cache_dir'].'/*' );
        }else
            _file::rm($this->_default_policy['cache_dir'],true);
    }


    public function setMulti ( array $items , $expiration=null){
        foreach($items as $k=>$v ){
            $this->set($k, $v);
        }
    }

    public function setMultiByTag ($tag , array $items , $expiration=null){
        foreach($items as $k=>$v ){
            $this->setByTag($tag, $k, $v);
        }
    }
    
    public function getMulti ( array $keys , $expiration=null ){
        $items = array();
        foreach($keys as $key ){
            if($value = $this->get($key)){
                $items[$key] = $value;
            }
        }

        return $items;
    }

    public function getMultiByTag ($tag, array $keys , $expiration=null){
        $items = array();
        foreach($keys as $key ){
            if($value = $this->getByTag($tag, $key)){
                $items[$key] = $value;
            }
        }

        return $items;
    }


    public function getVersion (){
        return 2.31;
    }

    /**
     * 递减 key
     * @param mixed $key 
     */
    public function decrement($key, $offset=1){
        if($value = $this->get($key) ){
            if( is_numeric($value) ){
                $value -= $offset;
                
                return (0>$value) ? 0 : $value;

            }else 
                return 0;
        }else
            return false;
    } 
    
    /**
     * 确定缓存文件名
     *
     * @param string $id 缓存id
     * @param string $tag 标签
     *
     * @return string
     */
    protected function _path($id, $tag='default'){
        $real_dir = $this->_default_policy['cache_dir'];
        
        $depth = $this->_default_policy['dir_depth']; 
        if ( 1 < $depth){
            //自动分割缓存目录
            $split_length = ceil(32/$depth);
            $dirs = str_split(md5($id), $split_length);
            $filename = array_pop($dirs).'.php';
            $filepath = implode(DIRECTORY_SEPARATOR, $dirs);

        }else{ //普通方式
    	    $path = pathinfo($id);
            if ( $this->_default_policy['encoding_key'] ){
			    $filename = md5( $path['basename'] ) . '.php';
		    }else{
			    $filename = preg_replace("/[[:punct:]]|[[:space:]]/", '', $path['basename']) . '.php';
		    }	
            $filepath = $path['dirname'];
        }

        $filepath = $tag.DIRECTORY_SEPARATOR.$filepath;
        $real_dir = _file::check($filepath, $real_dir);
        return $real_dir . DIRECTORY_SEPARATOR . $filename;
    }

    /**
     * 返回有效的策略选项
     *
     * @param array $policy
     * @return array
     */
    protected function _policy(array $policy = null){
    	$my_policy = is_null($policy) ? $this->_default_policy : array_merge($this->_default_policy, $policy);
    	if(!is_dir($my_policy['cache_dir']) or !is_writable($my_policy['cache_dir'])){
    		throw new RuntimeException("Please Fix Cache Directory({$my_policy['cache_dir']})!");
    	}else{
    		$my_policy['cache_dir'] = rtrim($my_policy['cache_dir'], '\\/');
    	}
        return $my_policy;
    }

}//END phpfiled

